#include "DrawFrame.h"

mini_engine::DrawFrame *mini_engine::DrawFrame::m_drawframe = nullptr;

mini_engine::DrawFrame::DrawFrame(/* args */)
{
}

void mini_engine::DrawFrame::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
{
    // cout << "begin recording command buffer" << endl;

    VkCommandBufferBeginInfo beginInfo{};
    beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;

    if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to begin recording command buffer!");
    }

    VkRenderPassBeginInfo renderPassInfo{};
    renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
    renderPassInfo.renderPass = RenderPass::renderPass;
    renderPassInfo.framebuffer = Framebuffers::swapChainFramebuffers[imageIndex];
    renderPassInfo.renderArea.offset = {0, 0};
    renderPassInfo.renderArea.extent = SwapChain::swapChainExtent;
    ;

    VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
    renderPassInfo.clearValueCount = 1;
    renderPassInfo.pClearValues = &clearColor;

    vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);

    vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, GraphicsPipeline::graphicsPipeline);

    VkViewport viewport{};
    viewport.x = 0.0f;
    viewport.y = 0.0f;
    viewport.width = (float)SwapChain::swapChainExtent.width;
    viewport.height = (float)SwapChain::swapChainExtent.height;
    viewport.minDepth = 0.0f;
    viewport.maxDepth = 1.0f;
    vkCmdSetViewport(commandBuffer, 0, 1, &viewport);

    VkRect2D scissor{};
    scissor.offset = {0, 0};
    scissor.extent = SwapChain::swapChainExtent;
    vkCmdSetScissor(commandBuffer, 0, 1, &scissor);

    // 推送常量
    SimplePushConstantData push{};
    push.offset = {0.0f, 0.0f};
    push.color = {0.0f, 1.0f, 0.0f};
    vkCmdPushConstants(commandBuffer,
                       GraphicsPipeline::pipelineLayout,
                       VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT,
                       0,
                       sizeof(SimplePushConstantData),
                       &push);

    VkBuffer vertexBuffers[] = {Model::vertexBuffer};
    VkDeviceSize offsets[] = {0};
    vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);

    vkCmdDraw(commandBuffer, static_cast<uint32_t>(vertices.size()), 1, 0, 0);

    vkCmdEndRenderPass(commandBuffer);

    if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to record command buffer!");
    }
}

void mini_engine::DrawFrame::drawFrame()
{
    // cout << "drawFrame" << endl;
    vkWaitForFences(LogicalDevice::logicalDevice, 1, &SyncObjects::inFlightFence, VK_TRUE, UINT64_MAX);
    vkResetFences(LogicalDevice::logicalDevice, 1, &SyncObjects::inFlightFence);

    uint32_t imageIndex;
    vkAcquireNextImageKHR(LogicalDevice::logicalDevice, SwapChain::swapChain, UINT64_MAX, SyncObjects::imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
    vkResetCommandBuffer(Command::commandBuffer, 0);
    recordCommandBuffer(Command::commandBuffer, imageIndex);
    VkSubmitInfo submitInfo{};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;

    VkSemaphore waitSemaphores[] = {SyncObjects::imageAvailableSemaphore};
    VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
    submitInfo.waitSemaphoreCount = 1;
    submitInfo.pWaitSemaphores = waitSemaphores;
    submitInfo.pWaitDstStageMask = waitStages;

    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &Command::commandBuffer;

    VkSemaphore signalSemaphores[] = {SyncObjects::renderFinishedSemaphore};
    submitInfo.signalSemaphoreCount = 1;
    submitInfo.pSignalSemaphores = signalSemaphores;

    if (vkQueueSubmit(LogicalDevice::graphicsQueue, 1, &submitInfo, SyncObjects::inFlightFence) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to submit draw command buffer!");
    }

    VkPresentInfoKHR presentInfo{};
    presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;

    presentInfo.waitSemaphoreCount = 1;
    presentInfo.pWaitSemaphores = signalSemaphores;

    VkSwapchainKHR swapChains[] = {SwapChain::swapChain};
    presentInfo.swapchainCount = 1;
    presentInfo.pSwapchains = swapChains;

    presentInfo.pImageIndices = &imageIndex;

    vkQueuePresentKHR(LogicalDevice::presentQueue, &presentInfo);
}

mini_engine::DrawFrame *mini_engine::DrawFrame::getDrawFrame()
{
    if (m_drawframe == nullptr)
    {
        m_drawframe = new DrawFrame();
        DrawFrameDestruction drawFrameDestruction;
    }
    return m_drawframe;
}

mini_engine::DrawFrame::~DrawFrame()
{
}
mini_engine::DrawFrame::DrawFrameDestruction::~DrawFrameDestruction()
{
    if (m_drawframe != nullptr)
    {
        delete m_drawframe;
        m_drawframe = nullptr;
    }
}